#include <iostream>
#include <stdio.h>
using namespace std;

extern "C"
{
    #include "libavcodec/avcodec.h"
    #include "libavformat/avformat.h"
    #include "libswscale/swscale.h"
    #include "libavdevice/avdevice.h"
    #include "libavutil/pixfmt.h"
    #include "libavutil/imgutils.h"
}
///由于我们建立的是C++的工程
///编译的时候使用的C++的编译器编译
///而FFMPEG是C的库
///因此这里需要加上extern "C"
///否则会提示各种未定义


///现在我们需要做的是让SaveFrame函数能把RGB信息定稿到一个PPM格式的文件中
void SaveFrame(uint8_t** data, int* linesizes, int width, int height,int index)
{
    FILE *pFile;
    char szFilename[32];

    // Open file
    sprintf(szFilename, "frame%d.ppm", index);
    pFile=fopen(szFilename, "wb");
    if(pFile==NULL)
    {
       return;
    }


    // Write ppm header
    fprintf(pFile, "P6 %d %d 255", width, height);

    // Write pixel data by line
    for(int y = 0; y < height; y++)
    {
        fwrite(data[0] + y * linesizes[0], 1, width*3, pFile);
    }

    // Close file
    fclose(pFile);
}



int main()
{
    const char* file_path = "D:/All Videos/testVideos/demo_1/newvideo.mp4";

    AVFormatContext* pFormatCtx = avformat_alloc_context();

    //读取avformat
    if (avformat_open_input(&pFormatCtx, file_path, nullptr, nullptr) < 0) {
        printf("can't open the file.");
        return -1;
    }

    //读取流信息，该函数会填充pFormatCtx->streams
    if (avformat_find_stream_info(pFormatCtx, nullptr) < 0) {
        printf("Could't find stream infomation.");
        return -1;
    }

    //dump格式信息
    av_dump_format(pFormatCtx, 0, file_path, 0);


    ///循环查找视频中包含的流信息，直到找到视频类型的流,暂不处理音频流
    int videoStream = -1;
    AVCodecParameters* pVideoCodecPar = nullptr;
    for (unsigned  i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = i;
        }
    }
    if (videoStream == -1) {
        printf("Didn't find a video stream.");
        return -1;
    }

    //AVCodecParameters包含视频流使用的codec的相关参数,我们就能找到具体的解码器并打开它。
    pVideoCodecPar = pFormatCtx->streams[videoStream]->codecpar;


    ///解码前的准备工作
    AVCodec* pVideoCodec = nullptr;
    AVCodecContext* pVideoCodecCtx = nullptr;

    //找到对应的解码器
    pVideoCodec = avcodec_find_decoder(pVideoCodecPar->codec_id);
    if (pVideoCodec == nullptr) {
        printf("videoCodec not found.");
        return -1;
    }

    //打开解码器
    //avcodec_open2前必须给解码器分配context
    pVideoCodecCtx = avcodec_alloc_context3(pVideoCodec);
    if (avcodec_parameters_to_context(pVideoCodecCtx, pVideoCodecPar) < 0) {
        printf("Could not copy parm to context.");
        return -1;
    }

    //使用pVideoCodec初始化pVideoCodecCtx，后续解码需要的上下文
    //为什么不一直使用pVideoCodec
    if(avcodec_open2(pVideoCodecCtx, pVideoCodec, nullptr) < 0)
    {
        printf("opne videoCodec failed");
        return -1;
    }

    ///缓存数据
    //需要一个载体来保存解码后的数据
    AVFrame* pFrame = av_frame_alloc();

    SwsContext* pSwsCtx = nullptr;
    uint8_t* data[4] = { nullptr };
    int  linesizes[4] = { 0 };

    //申请存储RGB的buffer
    if (av_image_alloc(data, linesizes, pVideoCodecCtx->width, pVideoCodecCtx->height,
                       AV_PIX_FMT_RGB24,16) < 0)
    {
        printf("alloc image failed");
        return -1;
    }

    //YUV2RGB前需要初始化sws_getContext
    pSwsCtx = sws_getContext(pVideoCodecCtx->width, pVideoCodecCtx->height,
            pVideoCodecCtx->pix_fmt, pVideoCodecCtx->width, pVideoCodecCtx->height,
            AV_PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);

    ///读取视频包并解码，转换，保存文件
    int index = 1;
    bool flag = false;
    AVPacket packet;

    while ( !flag )
    {
        if (av_read_frame(pFormatCtx, &packet) > 0)
        {
            flag = true;
        }
        //av_read_frame(pFormatCtx, &packet);
        if(packet.stream_index == videoStream)
        {
            avcodec_send_packet(pVideoCodecCtx, &packet);
            if (avcodec_receive_frame(pVideoCodecCtx, pFrame) == 0)
            {
                //格式转换
                sws_scale(pSwsCtx, pFrame->data, pFrame->linesize, 0, pFrame->height,
                          data, linesizes);

                //保存10张图片
                SaveFrame(data, linesizes, pVideoCodecCtx->width, pVideoCodecCtx->height, index);
                printf("Save frame %d\n", index);
                index += 1;
                if (index > 10) flag = true;
            } 

            av_packet_unref(&packet);
        }
    }

    av_free(data);
    av_free(pFrame);
    avcodec_close(pVideoCodecCtx);
    avformat_close_input(&pFormatCtx);

    cout << "Hello FFmpeg!" << endl;
    return 0;
}
